#include "game.h"

extern bool eink_scene_ready;
extern bool eink_flash_ready;

extern lv_obj_t *scr_eink;


static lv_obj_t *scr;
void tetris_create(void);

 static void gamepad_create(lv_obj_t *par)
 {

     char *btn_name[]={LV_SYMBOL_LEFT,LV_SYMBOL_RIGHT,LV_SYMBOL_UP,LV_SYMBOL_DOWN,"reset","A","B"};
     lv_obj_t *btn[7];


    
     for(uint8_t i=0;i<(sizeof(btn)/sizeof(btn[0]));i++)
     {
         btn[i] = lv_btn_create(par,NULL);
         lv_obj_set_size(btn[i],40,40);
         lv_obj_t *btn_label = lv_label_create(btn[i],NULL);
         lv_label_set_text(btn_label,btn_name[i]);
         lv_obj_align(btn_label,btn[i],LV_ALIGN_CENTER,0,0);

     }

    lv_obj_align(btn[2],par,LV_ALIGN_CENTER,0,0);
    lv_obj_align(btn[3],btn[2],LV_ALIGN_OUT_BOTTOM_MID,0,10);
    lv_obj_align(btn[0],btn[2],LV_ALIGN_OUT_LEFT_BOTTOM,-10,25);
    lv_obj_align(btn[1],btn[2],LV_ALIGN_OUT_RIGHT_BOTTOM,10,25);
    
    lv_obj_align(btn[5],par,LV_ALIGN_IN_BOTTOM_MID,0,-10);
    lv_obj_align(btn[4],par,LV_ALIGN_IN_BOTTOM_LEFT,10,-10);
    lv_obj_align(btn[6],par,LV_ALIGN_IN_BOTTOM_RIGHT,-10,-10);


 }

void game_create(lv_obj_t *obj, lv_event_t event)
{
    if(event==LV_EVENT_CLICKED)
    {
        scr = lv_obj_create(scr_eink,NULL);
        lv_obj_set_size(scr, 176, 264);


          tetris_create( );
        

        lv_obj_t *btn1 = lv_imgbtn_create(scr,NULL);
        lv_obj_t *btn2 = lv_imgbtn_create(scr,NULL);

        gamepad_create(scr);










        eink_scene_ready=false;
        eink_flash_ready=false;


    }
    

}
//Tetris
struct Piece {
  unsigned int *type;
  int x;
  int y;
  int dir;
};

struct Block {
  int x;
  int y;
};
//-------------------------------------------------------------------------
// game constants
//-------------------------------------------------------------------------
enum DIR {
  UP,
  RIGHT,
  DOWN,
  LEFT,
  MIN = UP,
  MAX = LEFT
};

const int nx = 10; // width of tetris court (in blocks)
const int ny = 12; // height of tetris court (in blocks)

const int rotateButton = 0;
const int leftButton = 1;
const int rightButton = 2;

const int audioPin1 = 3;
const int audioPin2 = 4;
//-------------------------------------------------------------------------
// game variables (initialized during reset)
//-------------------------------------------------------------------------
const int dx = (160 - 24) / nx;   // pixel size of a single tetris block
const int dy = 80 / ny;
int blocks[nx][ny];                               // 2 dimensional array (nx*ny) representing tetris court - either empty block or occupied by a 'piece'
bool playing = true;                              // true|false - game is in progress
bool lost = false;
Piece current, next;                              // the current and next piece
unsigned int score = 0;                           // the current score
int dir = 0;

//-------------------------------------------------------------------------
// tetris pieces
//
// blocks: each element represents a rotation of the piece (0, 90, 180, 270)
//         each element is a 16 bit integer where the 16 bits represent
//         a 4x4 set of blocks, e.g. j.blocks[0] = 0x44C0
//
//             0100 = 0x4 << 3 = 0x4000
//             0100 = 0x4 << 2 = 0x0400
//             1100 = 0xC << 1 = 0x00C0
//             0000 = 0x0 << 0 = 0x0000
//                               ------
//                               0x44C0
//
//-------------------------------------------------------------------------
unsigned int i[4] = {0x0F00, 0x2222, 0x00F0, 0x4444};
unsigned int j[4] = {0x44C0, 0x8E00, 0x6440, 0x0E20};
unsigned int l[4] = {0x4460, 0x0E80, 0xC440, 0x2E00};
unsigned int o[4] = {0xCC00, 0xCC00, 0xCC00, 0xCC00};
unsigned int s[4] = {0x06C0, 0x8C40, 0x6C00, 0x4620};
unsigned int t[4] = {0x0E40, 0x4C40, 0x4E00, 0x4640};
unsigned int z[4] = {0x0C60, 0x4C80, 0xC600, 0x2640};


//------------------------------------------------
// do the bit manipulation and iterate through each
// occupied block (x,y) for a given piece
//------------------------------------------------
void getPositionedBlocks(unsigned int type[], int x, int y, int dir, Block* positionedBlocks) {
 unsigned int bit;
 int blockIndex = 0, row = 0, col = 0, blocks = type[dir];

 for(bit = 0x8000; bit > 0 ; bit = bit >> 1) {
   if (blocks & bit) {
     Block block = {x+col, y+row};

     positionedBlocks[blockIndex++] = block;
   }
   if (++col == 4) {
     col = 0;
     ++row;
   }
 }
}
//-------------------------------------------------------------------------
// GAME LOGIC
//-------------------------------------------------------------------------

void lose() { playing = false; lost = true; /* pt.tune_stopscore(); */ }

void addScore(int n) { score = score + n; }
void clearScore() { score = 0; }
int getBlock(int x, int y) { return (blocks[x] ? blocks[x][y] : NULL); }
void setBlock(int x, int y, int type) { blocks[x][y] = type; }
void clearBlocks() { for(int x = 0; x < nx; x++) { for(int y = 0; y < ny; y++) { blocks[x][y] = NULL; } } }
void setCurrentPiece(Piece piece) {current = piece;}
void setNextPiece(Piece piece) {next = piece;}
//-----------------------------------------------------
// check if a piece can fit into a position in the grid
//-----------------------------------------------------
bool occupied(unsigned int type[], int x, int y, int dir) {
 bool result = false;

 Block positionedBlocks[4];
 getPositionedBlocks(type, x, y, dir, positionedBlocks);

 for (int i = 0; i < 4; i++) {
   Block block = positionedBlocks[i];

   if ((block.x < 0) || (block.x >= nx) || (block.y < 0) || (block.y >= ny) || getBlock(block.x, block.y))
     result = true;
 }

 return result;
}

bool unoccupied(unsigned int type[], int x, int y, int dir) {
 return !occupied(type, x, y, dir);
}

//-----------------------------------------
//-----------------------------------------
unsigned int *pieces[] = {i,j,l,o,s,t,z};

Piece randomPiece() {
 Piece current = {pieces[random(0, 6)], 3, 0, UP};
 return current;
}

void reset() {
 lost = false;
 playing = true;
 clearBlocks();
 clearScore();
 setCurrentPiece(randomPiece());
 setNextPiece(randomPiece());
}

bool move(int dir) {
 int x = current.x, y = current.y;
 switch(dir) {
   case RIGHT: x = x + 1; break;
   case LEFT:  x = x - 1; break;
   case DOWN:  y = y + 1; break;
 }
 if (unoccupied(current.type, x, y, current.dir)) {
   current.x = x;
   current.y = y;
   return true;
 }
 else {
   return false;
 }
}

void rotate() {
  int newdir = (current.dir == MAX ? MIN : current.dir + 1);
  if (unoccupied(current.type, current.x, current.y, newdir)) {
    current.dir = newdir;
  }
}




//-------------------------------------------------------------------------
// RENDERING
//-------------------------------------------------------------------------
void removeLine(int n) {
 int x, y;
 for(y = n; y >= 0; --y) {
   for(x = 0; x < nx; ++x) {
     setBlock(x, y, (y==0) ? NULL : getBlock(x, y-1));
   }
 }
}
void dropPiece() {
 Block positionedBlocks[4];
 getPositionedBlocks(current.type, current.x, current.y, current.dir, positionedBlocks);

 for (int i = 0; i < 4; i++) {
   setBlock(positionedBlocks[i].x, positionedBlocks[i].y, -1);
 }
}

void removeLines() {
 int x, y, n = 0;
 bool complete;

 for (y = ny; y > 0; --y) {
   complete = true;

   for(x = 0; x< nx; ++x) {
     if (!getBlock(x, y)) complete = false;
   }

   if (complete) {
     removeLine(y);
     y = y + 1; // recheck same line
     n++;
   }
 }
 if (n > 0) {
   addScore(100*pow(2, n-1)); // 1: 100, 2: 200, 3: 400, 4: 800
 }
}
void drawBlock(int x, int y) {
    tft.fillRect(x*dx, y*dy, dx, dy,TFT_GREENYELLOW);
}

void drawPiece(unsigned int type[], int x, int y, int dir) {
 Block positionedBlocks[4];
 getPositionedBlocks(type, x, y, dir, positionedBlocks);

 for (int i = 0; i < 4; i++) {
   drawBlock(positionedBlocks[i].x, positionedBlocks[i].y);
 }
}




void drawCourt() {
//  uView.clear(PAGE);
tft.fillRect(0,0,60,40,TFT_BLACK);
tft.fillRect(80,0,160,80,TFT_BLACK);
   drawPiece(current.type, current.x + 6, current.y, current.dir);

 int x, y;
 for(y = 0 ; y < ny ; y++) {
   for (x = 0 ; x < nx ; x++) {
     if (getBlock(x, y))
       drawBlock(x + 6, y);
   }
 }

 if (lost) {
   tft.setCursor(0, 20);
   tft.print(" Game Over ");
 }
}

void drawNext() {
 if (playing)
   drawPiece(next.type, 0, 3, next.dir);
}

void drawScore() {
 tft.setCursor(0, 0);
 tft.print(score);
}


void drop() {
 if (!move(DOWN)) {
   addScore(10);
   dropPiece();
   removeLines();
   setCurrentPiece(next);
   setNextPiece(randomPiece());

   if (occupied(current.type, current.x, current.y, current.dir)) {
     lose();
   }
 }
}
void draw() {
 drawCourt();
 drawNext();
 drawScore();

 removeLines();
//  uView.display();
}

static int cnt=0;
void my_task(lv_task_t * task)
{
   
    cnt++;
    io.digitalWrite0(3,0);
    draw();
    io.digitalWrite0(3,1);
    if (playing && (cnt%5 == 0)) {
    //  if (!pt.tune_playing) {
    //    pt.tune_playscore(tetrisScore);
    //  }

     drop();

     if (lost) {
       io.digitalWrite0(3,0);  
       draw();
       io.digitalWrite0(3,1);
       delay(1000);
       lost=false;
     }
   }
 

}

void tetris_create(void)
{
    io.digitalWrite0(3,0);
    tft.fillScreen(TFT_BLACK);
    reset();
    io.digitalWrite0(3,1);
    cnt = 0;
    lv_task_t * task = lv_task_create(my_task, 1000, LV_TASK_PRIO_MID,NULL);
    lv_task_set_period(task,100);


}